﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace CTP.Utilty
{
    /// <summary>
    /// 功能：日志记录，接收CTP传递进来的日志记录，并按配置文件中的配置
    ///       写入到文件中。日志采用异步写入方式。
    /// 创建：陈粤雄 20120429
    /// </summary>
    internal class Logger
    {
        #region 字段
        /// <summary>
        /// 静态单例
        /// </summary>
        private static Logger m_Instance;

        /// <summary>
        /// 当前缓存命令的队列，是线程安全的
        /// </summary>
        private ConcurrentQueue<LogItem> m_logQueue = new ConcurrentQueue<LogItem>();

        /// <summary>
        /// 命令出队列线程
        /// </summary>
        private Thread m_dequeueThread;

        /// <summary>
        /// 日志文件名
        /// </summary>
        private const string LOG_FILE_NAME = "ctp.log";

        private const int PROC_SLEEP_TIME = 500;
        #endregion

        #region 属性
        /// <summary>
        /// 获取配置类的单例
        /// </summary>
        public static Logger Instance { get { return m_Instance; } }
        #endregion

        #region 构造
        static Logger()
        {
            m_Instance = new Logger();
        }

        private Logger()
        {
            m_dequeueThread = new Thread(new ThreadStart(procQueue));
            m_dequeueThread.IsBackground = true;
            m_dequeueThread.Start();
        }
        #endregion

        #region 公开方法
        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="logItem"></param>
        public void Write(LogItem logItem)
        {
            // 不符合配置中的日志，直接丢弃
            if (logItem.LogType == LogTypes.Debug && !Config.Instance.IsLogDebug)
                return;
            if (logItem.LogType == LogTypes.Error && !Config.Instance.IsLogError)
                return;
            if (logItem.LogType == LogTypes.Info && !Config.Instance.IsLogInfo)
                return;

            m_logQueue.Enqueue(logItem);
        }

        /// <summary>
        /// 写入调试日志
        /// </summary>
        /// <param name="message"></param>
        public void WriteDebug(string message)
        {
            Write(new LogItem(message, "Default", LogTypes.Debug));
        }

        /// <summary>
        /// 写入信息日志
        /// </summary>
        /// <param name="message"></param>
        public void WriteInfo(string message)
        {
            Write(new LogItem(message, "Default", LogTypes.Info));
        }

        /// <summary>
        /// 写入错误日志
        /// </summary>
        /// <param name="message"></param>
        public void WriteError(string message)
        {
            Write(new LogItem(message, "Default", LogTypes.Error));
        }
        #endregion

        #region 私有方法

        /// <summary>
        /// 日志处理
        /// </summary>
        private void procQueue()
        {
            LogItem logItem;
            while (true)
            {
                string logText = "";
                while (m_logQueue.TryDequeue(out logItem))
                {
                    logText += logItem.ToString();
                }

                if (logText != "")
                {
                    writeToFile(logText);
                }

                Thread.Sleep(PROC_SLEEP_TIME);
            }
        }

        /// <summary>
        /// 写入日志到文件
        /// </summary>
        /// <param name="content"></param>
        private void writeToFile(string content)
        {
            Directory.CreateDirectory("log");
            string logPath = Path.Combine(Config.Instance.LogDir, LOG_FILE_NAME);
            long fileSize = 0;
            using (FileStream fs = new FileStream(logPath, FileMode.OpenOrCreate, FileAccess.Write))
            {
                fileSize = fs.Length;
                fs.Seek(fs.Length, SeekOrigin.Begin);
                byte[] buff = Encoding.UTF8.GetBytes(content);
                fs.Write(buff, 0, buff.Length);
            }

            // 如果文件超过最大大小，则转移到新的文件
            if (fileSize >= Config.Instance.LogRollFileSize)
            {
                File.Move(logPath, getRollNewFilePath());
            }
        }

        /// <summary>
        /// 获取滚动文件的新文件名
        /// </summary>
        /// <returns></returns>
        private string getRollNewFilePath()
        {
            int currentID = 1;
            while (true)
            {
                string fileName = string.Format("ctp_{0}_{1}.log", DateTime.Now.ToString("yyyy-MM-dd"), currentID);
                string path = Path.Combine(Config.Instance.LogDir, fileName);

                if (!File.Exists(path))
                {
                    return path;
                }
                else
                {
                    currentID++;
                }
            }
        }
        #endregion
    }

    #region 日志项
    /// <summary>
    /// 日志项
    /// </summary>
    internal class LogItem
    {
        /// <summary>
        /// 日志消息 
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// 日志目录
        /// </summary>
        public string Categote { get; set; }

        /// <summary>
        /// 日志类型
        /// </summary>
        public LogTypes LogType { get; set; }

        /// <summary>
        /// 构造默认日志项
        /// </summary>
        public LogItem()
        {
        }

        /// <summary>
        /// 以指定参数构造日志项
        /// </summary>
        /// <param name="message"></param>
        /// <param name="categote"></param>
        public LogItem(string message, string categote, LogTypes logType)
        {
            Message = message;
            Categote = categote;
            LogType = logType;
        }

        public override string ToString()
        {
            if (string.IsNullOrEmpty(Categote))
                return string.Format("{0} \tType:{1} \tMessage:{2}\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), LogType.ToString(), Message);
            else
                return string.Format("{0} \tType:{1} \tCategote:{2} \tMessage:{3}\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), LogType.ToString(), Categote, Message);
        }
    }

    /// <summary>
    /// 日志类型
    /// </summary>
    internal enum LogTypes { Debug, Info, Error }
    #endregion
}
